package com.cryptape.cita_wallet.service.http;

import android.content.Context;
import android.util.Log;

import com.cryptape.cita.protocol.core.methods.response.AppMetaData;
import com.cryptape.cita_wallet.constant.ConstantUtil;
import com.cryptape.cita_wallet.item.Token;
import com.cryptape.cita_wallet.item.Wallet;
import com.cryptape.cita_wallet.item.response.CITAERC20Transaction;
import com.cryptape.cita_wallet.item.response.CITATransaction;
import com.cryptape.cita_wallet.item.response.EthTokenSupply;
import com.cryptape.cita_wallet.item.response.EthTransaction;
import com.cryptape.cita_wallet.item.response.EthTransactionStatus;
import com.cryptape.cita_wallet.item.transaction.RestTransaction;
import com.cryptape.cita_wallet.util.CurrencyUtil;
import com.cryptape.cita_wallet.util.NumberUtil;
import com.cryptape.cita_wallet.util.db.DBWalletUtil;
import com.cryptape.cita_wallet.util.ether.EtherUtil;
import com.cryptape.cita_wallet.util.url.HttpCITAUrls;
import com.google.gson.Gson;
import com.mangoworld.android.BuildConfig;

import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

/**
 * Created by duanyytop on 2018/10/15
 */
public class HttpService {

    private static final int OFFSET = 20;

    private static OkHttpClient mOkHttpClient;

    public static OkHttpClient getHttpClient() {
        try {
            if (mOkHttpClient == null) {
                if (BuildConfig.IS_DEBUG) {
                    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
                    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
                    mOkHttpClient = new OkHttpClient.Builder()
                            .addInterceptor(logging)
                            .addInterceptor(new RetryIntercepter(2))//重试
                            .readTimeout(60, TimeUnit.SECONDS)
                            .connectTimeout(60, TimeUnit.SECONDS)
//                        .retryOnConnectionFailure(true)
                            .build();
                } else {
                    mOkHttpClient = new OkHttpClient.Builder()
                            .addInterceptor(new RetryIntercepter(2))//重试
                            .readTimeout(60, TimeUnit.SECONDS)
                            .connectTimeout(60, TimeUnit.SECONDS)
                            .build();
                }
            }
        } catch (Exception e) {
            throw (e);
        }
        return mOkHttpClient;
    }

    public static Observable<List<RestTransaction>> getEtherTransactionList(Context context, int page) {
        return Observable.just(DBWalletUtil.getCurrentWallet(context)).flatMap(new Func1<Wallet, Observable<List<RestTransaction>>>() {
            @Override
            public Observable<List<RestTransaction>> call(Wallet wallet) {
                try {
                    String ethUrl = String.format(EtherUtil.getEtherTransactionUrl(), wallet.address, page, OFFSET);
                    final Request ethRequest = new Request.Builder().url(ethUrl).build();
                    Call ethCall = HttpService.getHttpClient().newCall(ethRequest);
                    EthTransaction response = new Gson().fromJson(ethCall.execute().body().string(), EthTransaction.class);
                    List<RestTransaction> transactionRepons = response.result;
                    for (RestTransaction item : transactionRepons) {
                        item.chainName = ConstantUtil.ETH_MAINNET;
                        item.value = (NumberUtil.getEthFromWeiForStringDecimal8(new BigInteger(item.value)));
                        item.symbol = ConstantUtil.ETH;
                        item.nativeSymbol = ConstantUtil.ETH;
                    }
                    return Observable.just(transactionRepons);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return Observable.just(null);
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }


    public static Observable<List<RestTransaction>> getEtherERC20TransactionList(Context context, Token token, int page) {
        return Observable.just(DBWalletUtil.getCurrentWallet(context)).flatMap(new Func1<Wallet, Observable<List<RestTransaction>>>() {
            @Override
            public Observable<List<RestTransaction>> call(Wallet wallet) {
                try {
                    String ethUrl = String.format(EtherUtil.getEtherERC20TransactionUrl(), token.getContractAddress(), wallet.address, page, OFFSET);
//                    String address = "0x4AdbC6Fe5b337BBF0728db300FF80f7977B25085";
//                    String ethUrl = String.format(EtherUtil.getEtherERC20TransactionUrl(), token.getContractAddress(), address, page, OFFSET);

                    final Request ethRequest = new Request.Builder().url(ethUrl).build();
                    Call ethCall = HttpService.getHttpClient().newCall(ethRequest);
                    EthTransaction response = new Gson().fromJson(ethCall.execute().body().string(), EthTransaction.class);
                    List<RestTransaction> transactionRepons = response.result;
                    for (RestTransaction item : transactionRepons) {
                        item.chainName = ConstantUtil.ETH_MAINNET;
                        item.value = (NumberUtil.divideDecimalSub(new BigDecimal(item.value), token.decimals));
                        item.symbol = token.getSymbol();
                        item.nativeSymbol = ConstantUtil.ETH;
                    }
                    return Observable.just(transactionRepons);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return Observable.just(null);
            }
        }).subscribeOn(Schedulers.io());
//                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<List<RestTransaction>> getEtherERC20TransactionList(Context context, Token token) {
        return Observable.just(DBWalletUtil.getCurrentWallet(context)).flatMap(new Func1<Wallet, Observable<List<RestTransaction>>>() {
            @Override
            public Observable<List<RestTransaction>> call(Wallet wallet) {
                try {
                    String ethUrl = String.format(EtherUtil.getEtherERC20TransactionUrl(), token.getContractAddress(), wallet.address, 0, 100);

                    final Request ethRequest = new Request.Builder().url(ethUrl).build();
                    Call ethCall = HttpService.getHttpClient().newCall(ethRequest);
                    EthTransaction response = new Gson().fromJson(ethCall.execute().body().string(), EthTransaction.class);
                    List<RestTransaction> transactionRepons = response.result;
                    for (RestTransaction item : transactionRepons) {
                        item.chainName = ConstantUtil.ETH_MAINNET;
                        item.value = (NumberUtil.divideDecimalSub(new BigDecimal(item.value), token.decimals));
                        item.symbol = token.getSymbol();
                        item.nativeSymbol = ConstantUtil.ETH;
                    }
                    return Observable.just(transactionRepons);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return Observable.just(null);
            }
        }).subscribeOn(Schedulers.io());
//                .observeOn(AndroidSchedulers.mainThread());
    }

    public static Observable<List<RestTransaction>> getEtherERC20TransactionList(Context context, Token token, String address) {
        return Observable.fromCallable(new Callable<List<RestTransaction>>() {
            @Override
            public List<RestTransaction> call() throws Exception {
                try {
                    String ethUrl = String.format(EtherUtil.getEtherERC20TransactionUrl(), token.getContractAddress(), address, 0, 100);
                    final Request ethRequest = new Request.Builder().url(ethUrl).build();
                    Call ethCall = HttpService.getHttpClient().newCall(ethRequest);
                    EthTransaction response = new Gson().fromJson(ethCall.execute().body().string(), EthTransaction.class);
                    List<RestTransaction> transactionRepons = response.result;
                    for (RestTransaction item : transactionRepons) {
                        item.chainName = ConstantUtil.ETH_MAINNET;
                        item.value = (NumberUtil.divideDecimalSub(new BigDecimal(item.value), token.decimals));
                        item.symbol = token.getSymbol();
                        item.nativeSymbol = ConstantUtil.ETH;
                    }
                    return transactionRepons;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }


    public static Observable<List<RestTransaction>> getCITATransactionList(Context context, int page) {
        Wallet wallet = DBWalletUtil.getCurrentWallet(context);
        return Observable.fromCallable((Callable<AppMetaData.AppMetaDataResult>) () -> {
            CITARpcService.init(context, HttpCITAUrls.CITA_NODE_URL);
            return Objects.requireNonNull(CITARpcService.getMetaData()).getAppMetaDataResult();
        }).flatMap(new Func1<AppMetaData.AppMetaDataResult, Observable<List<RestTransaction>>>() {
            @Override
            public Observable<List<RestTransaction>> call(AppMetaData.AppMetaDataResult result) {
                try {
                    String citaUrl = String.format(HttpCITAUrls.CITA_TRANSACTION_URL, wallet.address, page + 1, OFFSET);
                    final Request citaRequest = new Request.Builder().url(citaUrl).build();
                    Call citaCall = HttpService.getHttpClient().newCall(citaRequest);

                    String res = citaCall.execute().body().string();

                    CITATransaction response = new Gson().fromJson(res, CITATransaction.class);
                    for (RestTransaction item : response.result.transactions) {
                        item.chainName = result.getChainName();
                        item.value = CurrencyUtil.fmtMicrometer(NumberUtil.getEthFromWeiForStringDecimal8(Numeric.toBigInt(item.value)));
                        item.symbol = result.getTokenSymbol();
                        item.nativeSymbol = result.getTokenSymbol();
                    }
                    return Observable.just(response.result.transactions);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return Observable.just(null);
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }


    public static Observable<List<RestTransaction>> getCITAERC20TransactionList(Context context, Token token, int page) {
        Wallet wallet = DBWalletUtil.getCurrentWallet(context);
        return Observable.fromCallable(new Callable<AppMetaData.AppMetaDataResult>() {
            @Override
            public AppMetaData.AppMetaDataResult call() {
                CITARpcService.init(context, HttpCITAUrls.CITA_NODE_URL);
                return CITARpcService.getMetaData().getAppMetaDataResult();
            }
        }).flatMap(new Func1<AppMetaData.AppMetaDataResult, Observable<List<RestTransaction>>>() {
            @Override
            public Observable<List<RestTransaction>> call(AppMetaData.AppMetaDataResult result) {
                try {
                    String citaUrl = String.format(HttpCITAUrls.CITA_ERC20_TRANSACTION_URL, token.getContractAddress(), wallet.address, page, OFFSET);
                    final Request citaRequest = new Request.Builder().url(citaUrl).build();
                    Call citaCall = HttpService.getHttpClient().newCall(citaRequest);

                    CITAERC20Transaction response = new Gson().fromJson(citaCall.execute().body().string(), CITAERC20Transaction.class);
                    for (RestTransaction item : response.result.transfers) {
                        item.value = (NumberUtil.divideDecimalSub(new BigDecimal(item.value), token.decimals));
                        item.symbol = token.getSymbol();
                        item.nativeSymbol = result.getTokenSymbol();
                    }
                    return Observable.just(response.result.transfers);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return Observable.just(null);
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }


    public static EthTransactionStatus getEthTransactionStatus(String hash) {
        String citaUrl = String.format(EtherUtil.getEtherTransactionStatusUrl(), hash);
//        Log.e("MainActivity","getEthTransactionStatus citaUrl:"+citaUrl);
        final Request citaRequest = new Request.Builder().url(citaUrl).build();
        Call citaCall = HttpService.getHttpClient().newCall(citaRequest);
        try {
            String response = citaCall.execute().body().string();
//            Log.e("MainActivity","getEthTransactionStatus response:"+response+ "  hash:"+hash);
            return new Gson().fromJson(response, EthTransactionStatus.class);
//            return new Gson().fromJson(citaCall.execute().body().string(), EthTransactionStatus.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static EthTokenSupply getEthTokensupply() {
        String citaUrl = EtherUtil.getEtherERC20TokenSupplyUrl();
//        Log.e("MainActivity","getEthTransactionStatus citaUrl:"+citaUrl);
        final Request citaRequest = new Request.Builder().url(citaUrl).build();
        Call citaCall = HttpService.getHttpClient().newCall(citaRequest);
        try {
            String response = citaCall.execute().body().string();
//            Log.e("MainActivity","getEthTransactionStatus response:"+response+ "  hash:"+hash);
            return new Gson().fromJson(response, EthTokenSupply.class);
//            return new Gson().fromJson(citaCall.execute().body().string(), EthTransactionStatus.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public static EthTransactionStatus getEthTransactionStatusNew(String hash) {
        String citaUrl = String.format(EtherUtil.getEtherTransactionStatusUrl(), hash);
        Log.e("Transaction", "getEthTransactionStatus citaUrl:" + citaUrl);
        final Request citaRequest = new Request.Builder().url(citaUrl).build();
        Call citaCall = HttpService.getHttpClient().newCall(citaRequest);
        try {
            String response = citaCall.execute().body().string();
            Log.e("Transaction", "getEthTransactionStatus response:" + response + "  hash:" + hash);
            return new Gson().fromJson(response, EthTransactionStatus.class);
//            return new Gson().fromJson(citaCall.execute().body().string(), EthTransactionStatus.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


//    https://api-ropsten.etherscan.io/api&module=stats&action=tokensupply&contractaddress=0xb2054aDd212F19581aEB195FDa7644471a996134&apikey=YourApiKeyToken
//    https://api-ropsten.etherscan.io/api?module=stats&action=tokensupply&contractaddress=0xb2054aDd212F19581aEB195FDa7644471a996134&apikey=YourApiKeyToken
//    https://api-ropsten.etherscan.io/api&module=stats&action=tokensupply&contractaddress=0xb2054aDd212F19581aEB195FDa7644471a996134&apikey=YourApiKeyToken

//    https://api-ropsten.etherscan.io/api?module=stats&action=tokensupply&contractaddress=0xb2054aDd212F19581aEB195FDa7644471a996134&apikey=YourApiKeyToken
}
